package com.youth.utils;

import com.youth.executor.ExcelReaderThread;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.io.FileInputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Future;

/**
 * @ClassName ExcelDataImportUtil
 * @Description: TODO
 * @Author yjwu
 * @Date 2019/12/7 15:12
 **/
@Slf4j
public class ExcelReaderUtil {
    private static final String excel2003 = ".xls";
    private static final String excel2007 = ".xlsx";

    private static Map<String, List<String>> doOk = new HashMap<>();


    /**
     * @MethodName: getCellVal
     * @Description: TODO 获取cell值
     * @Param: * @param cell
     * @Return: {@link String}
     * @Author: yjwu
     * @Date: 2019/12/7 15:38
     **/
    public static Object getCellVal(Cell cell) {
        Object result = null;
        //格式化字符类型的数字。
        //DecimalFormat 类主要靠 # 和 0 两种占位符号来指定数字长度。0 表示如果位数不足则以 0 填充，# 表示只要有可能就把数字拉上这个位置
        DecimalFormat decimalFormat = new DecimalFormat("0");
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
        DecimalFormat decimalFormat1 = new DecimalFormat("0.00");
        switch (cell.getCellType()) {
            case STRING:
                result = cell.getRichStringCellValue().getString();
                break;
            case NUMERIC:
                if ("General".equals(cell.getCellStyle().getDataFormatString())) {
//                    result = decimalFormat.format(cell.getNumericCellValue());
                    result = cell.getNumericCellValue();
                } else {
                    result = 0;
                }
                break;
            case BOOLEAN:
                result = cell.getBooleanCellValue();
                break;
            case BLANK:
                result = "";
                break;
            default:
                break;
        }
        return result;
    }

    /**
     * @param fieldList excel列头字符集合
     * @MethodName: doFieldListOk
     * @Description: TODO 判断列头集合是否正确。
     * @Param: * @param beanName bean的name
     * @Return: {@link boolean}
     * @Author: yjwu
     * @Date: 2019/12/7 16:03
     **/
    public static boolean doFieldListOk(String beanName, List<String> fieldList) {
        //判断传入的bean属性字符集合是否正确
        boolean flag = true;
        Object bean = MySpringUtil.getBean(beanName);
        if (doOk.containsKey(beanName)) {
            flag = true;
        } else {
            //获取该类所有字段数组
            Field[] declaredFields = bean.getClass().getDeclaredFields();
            List<String> declaredFieldList = new ArrayList<>(declaredFields.length);

            if (declaredFields == null || declaredFields.length < 1) {
                flag = false;
            } else {
                for (Field field :
                        declaredFields) {
                    declaredFieldList.add(field.getName());
                }


                for (String string :
                        fieldList) {
                    if (declaredFieldList.indexOf(string) == -1) {
                        flag = false;
                        break;
                    }
                }
            }
        }
        if (flag) {
            doOk.put(beanName, fieldList);
        }
        return flag;
    }

    /**
     * @param row
     * @MethodName: rowToObj
     * @Description: TODO 将row转换为相应的对象
     * @Param: * @param beanName
     * @Return: {@link Object}
     * @Author: yjwu
     * @Date: 2019/12/7 16:05
     **/
    public static <T> T rowToObj(String beanName, Row row, Class<T> clazz) throws NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, InstantiationException {
        List<String> fieldList = doOk.get(beanName);
        if (fieldList != null && fieldList.size() > 0) {
            T bean = clazz.getDeclaredConstructor().newInstance();
//            T bean = MySpringUtil.getBean(beanName, clazz);
            int j = 0;
            for (int i = 0; i < fieldList.size(); i++) {
                j = i + 1;
                Field field = bean.getClass().getDeclaredField(fieldList.get(i));
                //取消属性的访问权限控制，即使private属性也可以进行访问和赋值。
                field.setAccessible(true);
                //获取cell值
                if (j <= row.getLastCellNum()) {
                    field.set(bean, getCellVal(row.getCell(j)));
                }
            }
            return bean;
        }
        return null;
    }

    /**
     * @param filePath
     * @MethodName: getWorkbook
     * @Description: TODO 获取excel处理对象。
     * @Return: {@link Workbook}
     * @Author: yjwu
     * @Date: 2019/12/7 16:29
     **/
    public static Workbook getWorkbook(String filePath) throws Exception {
        Workbook workbook = null;
        FileInputStream fileInputStream = new FileInputStream(filePath);
        String excelType = filePath.substring(filePath.lastIndexOf("."));
        if (excel2003.equals(excelType)) {
            workbook = new HSSFWorkbook(fileInputStream);
        } else if (excel2007.equals(excelType)) {
            workbook = new XSSFWorkbook(fileInputStream);
        } else {
            throw new Exception("解析的文件格式有误");
        }
        return workbook;
    }

    /**
     * @param filePath  excel数据地址。 首先将excel数据上传到服务器，然后在从服务器中读取该文件。
     * @param fieldList excel表格列头字符串集合
     * @param executor  线程池
     * @MethodName: getDataFromExcelFile
     * @Description: TODO 从excel表格中读取数据
     * @Param: * @param beanName 读取的excel数据对应的bean的名称
     * @Return: {@link Map< Integer, List< Object>>}
     * @Author: yjwu
     * @Date: 2019/12/7 18:41
     **/
    public static <T> List<T> getDataFromExcelFile(String beanName, String filePath, List<String> fieldList, ThreadPoolTaskExecutor executor, Class<T> clazz) throws Exception {
        List<T> resultList = new ArrayList<T>();
        if (StringUtils.isEmpty(filePath)) {
            throw new Exception("文件地址为空");
        } else {
            //1 得到excel文档和sheet
            Workbook workbook = getWorkbook(filePath);
            Sheet sheet = workbook.getSheetAt(0);
            // excel数据总行数
            int lastRowNum = sheet.getLastRowNum();
            //线程次数
            int threadNumber = 4;
            //每个线程需要处理的行数
            int rowNumberOfOneThread = lastRowNum / threadNumber;
            //创建计数器
            CountDownLatch countDownLatch = new CountDownLatch(threadNumber);
            //2 存放callable返回的future结果
            List<Future> futureList = new ArrayList<>(threadNumber);
            for (int i = 0; i < threadNumber; i++) {
                int benginRowNumber = i * rowNumberOfOneThread;
                int endRowNumber = (i + 1) * rowNumberOfOneThread;
                System.out.println("Thread.currentThread().getName()=" + Thread.currentThread().getName() + "，benginRowNumber=" + benginRowNumber + ",endRowNumber=" + endRowNumber);
                Future submit = executor.submit(new ExcelReaderThread(beanName, countDownLatch, sheet, benginRowNumber, endRowNumber, clazz));
                futureList.add(submit);
            }

            //处理future结果
            for (Future future :
                    futureList) {
                List<T> resultListOfOneThread = (List<T>) future.get();
                resultList.addAll(resultListOfOneThread);
            }
            //直到countdownLatch计数器减为0，才可以继续向下执行。
            countDownLatch.await();
        }
        return resultList;
    }

    /**
     * @param listSize
     * @MethodName: splitList
     * @Description: TODO 等分list
     * @Param: * @param list
     * @Return: {@link List< List<T>>}
     * @Author: yjwu
     * @Date: 2019/12/8 21:41
     **/
    public static <T> List<List<T>> splitList(List<T> list, int listSize) {
        List<List<T>> listArray = new ArrayList<>();
        for (int i = 0; i < list.size(); i += listSize) {
            int toIndex = i + listSize > list.size() ? list.size() : i + listSize;
            listArray.add(list.subList(i, toIndex));
        }
        return listArray;
    }

}
